package api

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/go-caipu-team/go-caipu-plugin-sdk/backend"
	"github.com/goccy/go-json"
	"go-caipu/pkg/api/helper"
	"go-caipu/pkg/constant"
	"go-caipu/pkg/services/monitor/onlineuser"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/mssola/useragent"
	"go-caipu/pkg/api/jwt"
	"go-caipu/pkg/services/admin/auth"
	loginlog "go-caipu/pkg/services/admin/systemlog"
	"go-caipu/pkg/services/admin/sysuser"
	"go-caipu/pkg/util"
)

const (
	loginTypeSocial  = "social"
	loginTypeAccount = "account"
)

// @Tags 用户
// @Summary User login
// @Description 用户登录
// @Accept json
// @Param request body auth.UserLoginCommand true "request"
// @Success 200 {object} map[string]interface{}
// @Router /auth/login [post]
func (hs *HTTPServer) LoginPost(c *gin.Context) {
	var req auth.UserLoginCommand
	if err := c.ShouldBindJSON(&req); err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	switch strings.ToLower(req.AuthType) {
	case loginTypeSocial:
		SocialLoginPost(hs, c, &req)
	case loginTypeAccount:
		AccountLogin(hs, c, &req)
	default:
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New("authType is invalid"))
	}
}

// SocialLogin 第三方登录
//业务逻辑：
//1.判断第三方登录通过，不通过登录失败
//2、登录成功则判断第三方是否绑定用户，如果未绑定则添加用户和绑定第三方登录信息
//3、登录成功已绑定用户则判断用户是否禁用，如果禁用则登录失败

func SocialLoginPost(hs *HTTPServer, c *gin.Context, req *auth.UserLoginCommand) {
	var err error

	req.IpAddress = c.RemoteIP() //获取客户端IP
	plugin, exists, _ := hs.pluginContextProvider.Get(context.Background(), req.Source)
	if !exists {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New(req.Source+" social not found"))
		return
	}
	pluginContext := backend.PluginContext{
		PluginID:                   plugin.PluginID,
		DataSourceInstanceSettings: plugin.DataSourceInstanceSettings,
	}
	pluginResp, err := hs.pluginClient.QueryData(c.Request.Context(), &backend.QueryDataRequest{
		PluginContext: pluginContext,
		Queries: []backend.DataQuery{
			{
				RefID:     "social",
				QueryType: "getLoginUser",
				JSON:      []byte("{\"code\":\"" + req.Code + "\"}"),
			},
		},
	})
	if err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	result := pluginResp.Responses["social"]
	if result.Error != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, result.Error)
		return
	}
	var data map[string]interface{}
	_ = json.Unmarshal(pluginResp.Responses["social"].Data, &data)
	//获取到用户信息，如果没有则添加用户并且绑定角色、第三方登录信息
	thirdUserId := data["userid"].(string)
	SysUserId, exists, err2 := hs.userService.UserThird(c.Request.Context(), sysuser.UserThirdReq{ThirdId: thirdUserId, ThirdType: req.Source})
	if err2 != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	//存在用户信息 ，则关联登录
	if exists {
		//获取用户信息
		user, err := hs.userService.GetUser(c.Request.Context(), sysuser.GetUserCommand{UserId: SysUserId})
		if err != nil {
			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
			return
		}
		hs.UserLoginHandle(c, &user, req.AuthType)
		return
	}
	//不存在用户信息，则添加用户
	pluginResp, err = hs.pluginClient.QueryData(c.Request.Context(), &backend.QueryDataRequest{
		PluginContext: pluginContext,
		Queries: []backend.DataQuery{
			{
				RefID:     "getUserInfo",
				QueryType: "getUserInfo",
				JSON:      []byte("{\"userid\":\"" + thirdUserId + "\"}"),
			},
		},
	})
	if err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	result = pluginResp.Responses["getUserInfo"]
	if result.Error != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, result.Error)
		return
	}
	//6位随机数
	password, _ := util.GetRandomString(8)
	_ = json.Unmarshal(pluginResp.Responses["getUserInfo"].Data, &data)
	model := sysuser.InsertUserCommand{
		UserId:   hs.Snowflake.Generate().Int64(),
		Username: strings.ToLower(thirdUserId),
		NickName: data["name"].(string),
		Password: password,
		Avatar:   data["avatar"].(string),
		Phone:    data["mobile"].(string),
		Email:    data["email"].(string),
		Sex:      util.StringToInt(data["gender"].(string)),
		Status:   1,
		RoleIds:  []string{"1828264722919587840"}, //普通用户角色
		DeptId:   1,
	}
	model.ThirdUser = sysuser.UserThirdReq{
		UserId:    model.UserId,
		ThirdId:   strings.ToLower(thirdUserId),
		ThirdType: strings.ToLower(req.Source),
	}
	err = hs.userService.Create(c.Request.Context(), model)
	if err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	user, err := hs.userService.GetUser(c.Request.Context(), sysuser.GetUserCommand{UserId: model.UserId})

	hs.UserLoginHandle(c, &user, req.AuthType)
}

// 获取 地址
func getLocation(hs *HTTPServer, ip string) string {
	if util.IsPrivateIP(ip) {
		return "内网"
	}
	
	plugin, exists, _ := hs.pluginContextProvider.Get(context.Background(), "ip2region")
	if exists {
		req := backend.QueryDataRequest{PluginContext: backend.PluginContext{
			PluginID:                   plugin.PluginID,
			DataSourceInstanceSettings: plugin.DataSourceInstanceSettings,
		}}
		req.Queries = append(req.Queries, backend.DataQuery{

			RefID: "ip2region",
			JSON:  json.RawMessage(fmt.Sprintf(`{"ip":"%s"}`, ip)),
		})
		response, err := hs.pluginClient.QueryData(context.Background(), &req)
		if err == nil {
			result := response.Responses["ip2region"]
			if result.Status == 200 {
				var location Location
				_ = json.Unmarshal(result.Data, &location)
				return location.Location
			}
		} else {
			return err.Error()
			hs.log.Error("plugin:ip2region", "err", err)
		}
	}
	return "未知"
}

// AccountLogin 用户密码登录
func AccountLogin(hs *HTTPServer, c *gin.Context, req *auth.UserLoginCommand) {

	if !captchaVerifyHandler(req.UUID, req.Captcha) {
		helper.ErrorWithDetail(c, constant.CodeAuth, constant.ErrCaptchaCode, nil)
		return
	}
	req.Password = hs.Cfg.DecryptedData(req.Password)
	req.IpAddress = c.RemoteIP()

	user, err := hs.loginService.AuthenticateUser(c.Request.Context(), req)
	if err != nil {
		if errors.Is(err, auth.ErrUserDisabled) {
			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrUserDisabled, err)
			return
		}
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrUserPassword, err)
		return
	}
	hs.UserLoginHandle(c, user, req.AuthType)
}
func (hs *HTTPServer) UserLoginHandle(c *gin.Context, user *sysuser.User, authType string) {
	var err error
	var log = &loginlog.SysLoginLog{Msg: "帐户密码", Username: user.NickName}
	log.CreateBy = user.UserId

	defer func() {
		if err != nil {
			log.Remark = err.Error()
		}
		go pushLoginLog(c, hs, log)
	}()

	switch strings.ToLower(authType) {
	case loginTypeSocial:
		log.Msg = "第三方登录"
		log.Username = "扫码"
	case loginTypeAccount:
		log.Msg = "帐号密码"
	default:
		log.Msg = "未知"
	}
	//获取角色权限
	permission := []string{"*"}
	if !util.Contains(user.RoleIds, "1828271398028574720") {
		result, err := hs.roleService.GetPermissionByRole(user.RoleIds)
		if err != nil {
			helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
			return
		}
		permission = result
	}

	token, err := hs.generateToken(user.UserId)
	if err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
		return
	}
	location := getLocation(hs, util.GetClientIP(c))

	//添加日志
	ua := useragent.New(c.Request.UserAgent())
	browserName, browserVersion := ua.Browser()
	log.LoginLocation = location

	log.Status = 1
	//添加在线用户
	hs.onlineService.Set(context.Background(), onlineuser.OnlineCommand{
		UserId:   util.Int64ToString(user.UserId),
		Token:    token,
		UserName: user.Username,
		NickName: user.NickName,
		Ip:       util.GetClientIP(c),
		Browser:  browserName + " " + browserVersion,
		Os:       ua.OS(),
		Location: location,
	})
	//返回登录结果
	helper.SuccessWithData(c, map[string]interface{}{
		"token":        token,
		"username":     user.Username,
		"roles":        user.RoleIds,
		"auths":        permission,
		"refreshToken": token,
		"expire":       util.Int64ToString(hs.Jwt.TimeOut),
	})
}

type Location struct {
	Location string `json:"location"`
	Took     string `json:"took"`
}

func pushLoginLog(c *gin.Context, hs *HTTPServer, log *loginlog.SysLoginLog) {
	if log == nil {
		return
	}
	//添加登录消息内容
	ua := useragent.New(c.Request.UserAgent())
	browserName, browserVersion := ua.Browser()

	log.LoginTime = time.Now().Format("2006-01-02 15:04:05")
	log.Ipaddr = util.GetClientIP(c)
	log.Browser = browserName + " " + browserVersion
	log.Platform = ua.Platform()
	log.Os = ua.OS()

	hs.bus.Publish(context.Background(), log)
}

// @Tags 用户
// @Summary User login
// @Description 用户退出
// @Accept json
// @Param request body auth.UserLoginCommand true "request"
// @Success 200 {object} map[string]interface{}
// @Router /auth/logout [post]
func (hs *HTTPServer) LogoutPost(c *gin.Context) {
	//清除缓存
	tokenString := c.Request.Header.Get(jwt.AUTH)
	hs.onlineService.Remove(c.Request.Context(), hs.Jwt.GetTokenString(tokenString))
	hs.userService.RefreshUserInfo(c.Request.Context(), hs.getJwtUserID(c))

	helper.SuccessWithData(c, "success")
}

// generateToken 生成token
func (hs *HTTPServer) generateToken(userId int64) (string, error) {
	token, err := hs.Jwt.GenToken(userId, time.Now().Add(time.Second*time.Duration(hs.Jwt.TimeOut)))
	if err != nil {
		return "", errors.New("failed to generate JWT jwt")
	}

	return token, nil
}
func (hs *HTTPServer) RefreshToken(c *gin.Context) {
	helper.SuccessWithOutData(c)
}

func captchaVerifyHandler(Id string, value string) bool {
	if store.Verify(Id, value, true) {
		return true
	}
	return false
}

// GetSocialLogin 第三方登录
func (hs *HTTPServer) GetSocialLogin(c *gin.Context) {
	social := c.Param("source")
	plugin, exists, _ := hs.pluginContextProvider.Get(c.Request.Context(), social)
	if !exists {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, errors.New(social+" social not found"))
		return
	}
	req := backend.QueryDataRequest{PluginContext: backend.PluginContext{
		PluginID:                   plugin.PluginID,
		DataSourceInstanceSettings: plugin.DataSourceInstanceSettings,
	}}
	req.Queries = append(req.Queries, backend.DataQuery{
		RefID:     "social",
		QueryType: "loginUrl",
	})
	result, err := hs.pluginClient.QueryData(context.Background(), &req)
	if err != nil {
		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
	}
	var data map[string]string
	_ = json.Unmarshal(result.Responses["social"].Data, &data)
	helper.SuccessWithData(c, data)
}
